package com.stone.serverless.web;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.ReflectUtil;
import cn.hutool.extra.spring.SpringUtil;
import com.stone.serverless.sdk.constant.ClassPathConstant;
import com.stone.serverless.sdk.event.ComponentRefresh;
import com.stone.serverless.sdk.event.ComponentRunner;
import com.stone.serverless.web.domain.RouterInfo;
import com.stone.serverless.web.domain.ServerlessClassRequestMappingInfo;
import com.stone.serverless.web.domain.ServerlessMethodRequestMappingInfo;
import com.stone.serverless.web.listener.ServletRequestHandledListener;
import lombok.SneakyThrows;

import java.lang.annotation.Annotation;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * @Description: TODO
 * @Author: MECHREVO
 * @Date: 2022/1/17 18:53
 * @Version: 1.0.0
 */
public class WebRunner implements ComponentRunner, ComponentRefresh, WebServer {

    public static WebRunner INSTANCE;

    public static StartWeb startWeb;

    @Override
    public void run(Map<String, Object> environment, String commonScan) {
        startWeb = new StartWeb();
        startWeb.start(environment, commonScan);
        WebComponentProvider.INSTANCE.add(this);
    }


    @Override
    public void stop() {
        startWeb.stop();
    }


    @SneakyThrows
    @Override
    public void registerRouters(List<Object> routerInfo, ClassLoader classLoader) {
        if (ObjectUtil.isNull(classLoader)) {
            return;
        }
        Class<? extends Annotation> requestMapping = (Class<? extends Annotation>) classLoader.loadClass(ClassPathConstant.SPRING_REQUEST_MAPPING_CLASS_PATH);

        RouterInfo ri = new RouterInfo();
        ri.setComponentName(ReflectUtil.invoke(classLoader, "getComponentName"));
        ri.setVersion(ReflectUtil.invoke(classLoader, "getVersion"));

        for (Object obj : routerInfo) {
            ServerlessClassRequestMappingInfo classServerlessRequestMappingInfo = this.buildRequestInfo(
                    obj.getClass(), requestMapping);

            if (ObjectUtil.isNull(classServerlessRequestMappingInfo)) {
                classServerlessRequestMappingInfo = new ServerlessClassRequestMappingInfo();
            }
            classServerlessRequestMappingInfo.setHandler(obj);

            List<ServerlessMethodRequestMappingInfo> list = new ArrayList<>();
            Method[] publicMethods = ReflectUtil.getPublicMethods(obj.getClass());
            for (Method publicMethod : publicMethods) {
                ServerlessClassRequestMappingInfo methodServerlessRequestMappingInfo =
                        this.buildRequestInfo(publicMethod, requestMapping);
                if (ObjectUtil.isNotNull(methodServerlessRequestMappingInfo)) {
                    list.add(new ServerlessMethodRequestMappingInfo(methodServerlessRequestMappingInfo, publicMethod));
                }
            }
            if (list.size() > 0) {
                classServerlessRequestMappingInfo.setMethodRequestMappingInfoList(list);
                ri.getRequestMappingInfo().add(classServerlessRequestMappingInfo);
            }
        }

        if (CollUtil.isEmpty(ri.getRequestMappingInfo())) {
            return;
        }
        startWeb.registerRouters(ri);
    }

    @SneakyThrows
    public ServerlessClassRequestMappingInfo buildRequestInfo(AnnotatedElement c,
                                                              Class<? extends Annotation> annotationType) {
        Class<?> bc = annotationType.getClassLoader().loadClass(ClassPathConstant.SPRING_ANNOTATION_UTILS_CLASS_PATH);

        Method getAnnotationAttributes = Arrays.stream(ReflectUtil.getMethods(bc, v -> {
            if (v.getName().equals("findMergedAnnotationAttributes")) {
                Class<?>[] parameterTypes = v.getParameterTypes();
                if (!parameterTypes[1].equals(String.class)) {
                    return true;
                }
            }
            return false;
        })).findFirst().orElse(null);
        Object annotationAttributes = ReflectUtil.invokeStatic(getAnnotationAttributes, c, annotationType, false, false);

        //Map<String, Object> annotationValue = AnnotationUtil.getAnnotationValueMap(c, mergedAnnotation.annotationType());
        if (ObjectUtil.isNotNull(annotationAttributes)) {
            ServerlessClassRequestMappingInfo serverlessRequestMappingInfo = new ServerlessClassRequestMappingInfo();
            serverlessRequestMappingInfo.setPaths(ReflectUtil.invoke(annotationAttributes, "getStringArray", "value"));
            Object[] requestMethod = ReflectUtil.invoke(annotationAttributes, "get", "method");
            if (requestMethod != null && requestMethod.length > 0) {
                serverlessRequestMappingInfo.setMethods(Arrays.stream(requestMethod).map(Object::toString).collect(Collectors.toList()));
            }
            serverlessRequestMappingInfo.setParams(ReflectUtil.invoke(annotationAttributes, "getStringArray", "params"));
            serverlessRequestMappingInfo.setHeaders(ReflectUtil.invoke(annotationAttributes, "getStringArray", "headers"));
            serverlessRequestMappingInfo.setConsumes(ReflectUtil.invoke(annotationAttributes, "getStringArray", "consumes"));
            serverlessRequestMappingInfo.setProduces(
                    ReflectUtil.invoke(annotationAttributes, "getStringArray", "produces"));
            serverlessRequestMappingInfo.setMappingName(
                    ReflectUtil.invoke(annotationAttributes, "getString", "name"));
            return serverlessRequestMappingInfo;
        }
        return null;
    }

    @Override
    public List<String> routerStatistics(String componentName, String version) {
        return startWeb.routerStatistics(componentName, version);
    }


    @Override
    public void componentUninstalled(ClassLoader moduleClassLoader) {
        String componentName = ReflectUtil.invoke(moduleClassLoader, "getComponentName");
        String version = ReflectUtil.invoke(moduleClassLoader, "getVersion");

        if (ObjectUtil.isNull(componentName) || ObjectUtil.isNull(version)) {
            return;
        }
        startWeb.uninstallRouters(componentName, version);
    }

    @Override
    public void componentInstalled(ClassLoader classLoader) {

    }

    @Override
    public void refresh() {
        ServletRequestHandledListener servletRequestHandledListener = SpringUtil.getBean(ServletRequestHandledListener.class);
        servletRequestHandledListener.setStoneHandlerInterceptor(WebComponentConsumer.INSTANCE.stoneHandlerInterceptor);

        ClassLoaderBeanNameMappingInfoHandlerMapping cl = SpringUtil.getBean(ClassLoaderBeanNameMappingInfoHandlerMapping.class);
        cl.setComponentNameMap(startWeb.getComponentNameMap());

        MvcConfiguration mvcConfiguration = SpringUtil.getBean(MvcConfiguration.class);

        ClassLoaderResourceResolver classLoaderResourceResolver = mvcConfiguration.getClassLoaderResourceResolver();
        classLoaderResourceResolver.setComponentNameMap(startWeb.getComponentNameMap());

        mvcConfiguration.getAuthInterceptor().setStoneHandlerInterceptor(WebComponentConsumer.INSTANCE.stoneHandlerInterceptor);
    }
}
